﻿using Microsoft.Extensions.Localization;
using PHMEE.Constants;
using PHMEE.EnergyEfficiency;
using PHMEE.Entities.BaseInfo.Points;
using PHMEE.Localization;
using PHMEE.ToolKits;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;

namespace PHMEE.Managers.BaseInfo.Points
{
    public class PointManager : DomainService, IPointManager
    {
        private readonly IStringLocalizer<PHMEEResource> L;
        private readonly IPointRepository pointRepository;
        private readonly IVesselRepository vesselRepository;
        private readonly string Language;

        public PointManager(IStringLocalizer<PHMEEResource> L, IPointRepository pointRepository, IVesselRepository vesselRepository)
        {
            this.L = L;
            this.pointRepository = pointRepository;
            this.vesselRepository = vesselRepository;
            this.Language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
        }

        public async Task<Point> CreatePoint(PointTypeEnum pointType, string name, string nameen, Guid ownerId)
        {
            var (expression, localizedName) = GetPointExpression(name, nameen, ownerId);

            var existingPoint = await pointRepository.FirstOrDefaultAsync(expression);
            if (existingPoint != null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.PointNameAlreadyExist], PHMEEDomainErrorCodes.PointNameAlreadyExist).WithData("Name", localizedName);
            }
            var id = GuidGenerator.Create();

            Point point = null;
            switch (pointType)
            {
                case PointTypeEnum.Acceleration:
                    point = new AcceleratePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Speed:
                    point = new SpeedPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Displacement:
                    point = new DisplacementPoint(id, name, nameen);
                    break;
                case PointTypeEnum.RevSpeed:
                    point = new RevSpeedPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Temperature:
                    point = new TemperaturePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Pressure:
                    point = new PressurePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Current:
                    point = new CurrentPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Voltage:
                    point = new VoltagePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Flow:
                    point = new FlowPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Level:
                    point = new LevelPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Longitude:
                    point = new LongitudePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Latitude:
                    point = new LatitudePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Draft:
                    point = new DraftPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Power:
                    point = new PowerPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Load:
                    point = new LoadPoint(id, name, nameen);
                    break;
                case PointTypeEnum.FuelConsumption:
                    point = new FuelConsumptionPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Torque:
                    point = new TorquePoint(id, name, nameen);
                    break;
                case PointTypeEnum.MainEngineStatus:
                    point = new MainEngineStatusPoint(id, name, nameen);
                    break;
                case PointTypeEnum.FuelType:
                    point = new FuelTypePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Angle:
                    point = new AnglePoint(id, name, nameen);
                    break;
                case PointTypeEnum.Viscosity:
                    point = new ViscosityPoint(id, name, nameen);
                    break;
                case PointTypeEnum.PowerFactor:
                    point = new PowerFactorPoint(id, name, nameen);
                    break;
                case PointTypeEnum.Frequency:
                    point = new FrequencyPoint(id, name, nameen);
                    break;
            }

            return point;
        }

        public (Expression<Func<Point, bool>> Expression, string LocalizedName) GetPointExpression(string name, string nameen, Guid ownerId)
        {
            Expression<Func<Point, bool>> expression = item => item.OwnerId == ownerId;

            string localizedName = string.Empty;

            switch (Language)
            {
                case PhmeeConst.LanguageEn:

                    localizedName = nameen;

                    expression = expression.And(item => item.NameEn == nameen);

                    break;
                default:

                    localizedName = name;

                    expression = expression.And(item => item.Name == name);

                    break;
            }

            return (expression, localizedName);
        }

        public Dictionary<int, object> GetPointCategories()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<PointCategoryEnum>();
        }

        public Dictionary<int, object> GetPointTypes(PointCategoryEnum pointCategory)
        {

            Dictionary<int, object> dict = PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<PointTypeEnum>();

            dict = dict.Where(item => Point.GetPointTypeOptions(pointCategory).Contains((PointTypeEnum)item.Key)).ToDictionary();

            return dict;
        }

        public Dictionary<int, object> GetPointPositions()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<PointPositionEnum>();
        }

        public Dictionary<int, object> GetPointDirections()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<PointDirectionEnum>();
        }

        public Dictionary<int, object> GetPointDiagnosisAlgs()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<DiagnosisAlgEnum>();
        }

        public async Task<Dictionary<int, object>> GetPointTagOptions(Guid ownerId, string ownerType)
        {
            var dict = new Dictionary<int, object>();

            var queryVessel = await vesselRepository.PhmeeGetQuery();

            switch (ownerType)
            {
                case nameof(Vessel):

                    var query = queryVessel.Where(item => item.Id == ownerId);

                    var vessel = query.FirstOrDefault();

                    if (vessel != null)
                    {
                        dict = PHMEECoreHelper.PhmeeGetEnumKeyDescriptions(vessel.GetVesselTagOptions());
                    }

                    break;
                case nameof(Unit):

                    var queryUnit = queryVessel.SelectMany(vessel => vessel.Units.Select(unit => new { Vessel = vessel, Unit = unit }));

                    queryUnit = queryUnit.Where(item => item.Unit.Id == ownerId);

                    var unit = queryUnit.FirstOrDefault()?.Unit;

                    if (unit != null)
                    {
                        dict = PHMEECoreHelper.PhmeeGetEnumKeyDescriptions(unit.GetUnitTagOptions());
                    }

                    break;
                case nameof(Equipment):

                    var queryEquipment = queryVessel.SelectMany(vessel => vessel.Units
                                         .SelectMany(unit => unit.Equipments
                                              .Select(equipment => new { Vessel = vessel, Unit = unit, Equipment = equipment })));

                    queryEquipment = queryEquipment.Where(item => item.Equipment.Id == ownerId);

                    var equipment = queryEquipment.FirstOrDefault()?.Equipment;

                    if (equipment != null)
                    {
                        dict = PHMEECoreHelper.PhmeeGetEnumKeyDescriptions(equipment.GetEquipmentTagOptions());
                    }

                    break;
                case nameof(Component):

                    var queryComponent = queryVessel.SelectMany(vessel => vessel.Units
                                         .SelectMany(unit => unit.Equipments
                                               .SelectMany(equipment => equipment.Components
                                                     .Select(component => new { Vessel = vessel, Unit = unit, Equipment = equipment, Component = component }))));

                    queryComponent = queryComponent.Where(item => item.Component.Id == ownerId);

                    var component = queryComponent.FirstOrDefault()?.Component;

                    if (component != null)
                    {
                        dict = PHMEECoreHelper.PhmeeGetEnumKeyDescriptions(component.GetComponentTagOptions());
                    }

                    break;
            }

            return dict;

        }

    }
}
